using System;
using System.Collections;
using System.Text.RegularExpressions;
using System.Text;

namespace HtmlParser
{
	public class Document
	{
        NodeCollection _allNodes;
        NodeCollection _rootNodes;

		public NodeCollection RootNodes
		{
			get { return _rootNodes; }
		}

		public NodeCollection AllNodes
		{
			get{ return _allNodes; }
		}

		public Document()
		{
		}

		public Document(string html)
		{
			Parse(html);
		}

		public void Parse(string html)
		{            
			ArrayList tags = new ArrayList();
            _allNodes = new NodeCollection();

			bool inTag = false;
			bool findEndToken = false;
			bool inSetupTag = false;
			bool inComment = false;
			
			char[] endToken = new char[1];

			string beforeText = "";

			char[] buffer = new char[html.Length];
			int bufferIndex = 0;

			Tags tagsInfo = new Tags();			

			Nodes.Element lastElement = null;
			foreach(char chr in html)
			{
				if (findEndToken)
				{
					buffer[bufferIndex] = chr;
					bufferIndex++;
					if (inSetupTag)
					{
                        //* Check if the tag starts with <!--
						if(Utils.StartsWith(buffer,"<!--".ToCharArray(),0))
						{
                            //* The tag starts with <!-- so it is not a setup tag but comment
							endToken = ("-->".ToCharArray());
							findEndToken = true;
							inSetupTag = false;
							inComment = true;
							inTag = false;							
						}
					}

					if (Utils.EndsWith(buffer,endToken,bufferIndex))
					{
						if (inTag)
						{
							beforeText = new string(buffer,0,bufferIndex - endToken.Length);
							Utils.CleanUpCA(buffer);
							Utils.PlaceString(buffer,endToken,0);
							bufferIndex = endToken.Length;
						}

						if((inSetupTag) || (inComment))
						{
							string temp = new string(buffer,0,bufferIndex);
							Utils.CleanUpCA(buffer);
							Utils.PlaceString(buffer,beforeText.ToCharArray(),0);
							Utils.PlaceString(buffer,temp.ToCharArray(),beforeText.Length);							
							bufferIndex = bufferIndex + beforeText.Length;
							beforeText = "";

							inSetupTag = false;
							inComment = false;
						}

						findEndToken = false;
					}
				}
				else if (chr == '<')
				{
                    //* A new tag has started.
					beforeText = new string(buffer,0,bufferIndex);

					bufferIndex = 0;
					Utils.CleanUpCA(buffer);
					buffer[bufferIndex] = chr;
					bufferIndex++;

					inTag = true;
				}
				else if (chr == '>')
				{
                    //* Check if in tag
					if (inTag)
					{
						//* Current tag has ended.
						inTag = false;

						buffer[bufferIndex] = chr;
						bufferIndex++;

						if (!beforeText.Equals(""))
						{                            
							_allNodes.Add(new HtmlParser.Nodes.Text(beforeText));
						}

						Nodes.Element element = new Nodes.Element(new string(buffer, 0, bufferIndex));
						tags.Add(element);						
						_allNodes.Add(element);

						lastElement = element;
						if(!element.IsEndElement)
						{
							HtmlParser.Tags.TagInfo tagInfo = tagsInfo.GetTagTypeInfo(element.TagName);

							if (tagInfo.Action != Tags.ParserAction.PARSE_HTML)
							{
								findEndToken = true;
								endToken = ("</" + lastElement.TagName).ToCharArray();									
								inTag = true; //* This does not do anything until the end token has been found
							}

							if (tagInfo.Type == Tags.TagType.STANDALONE)
							{
								element.IsStandAlone = true;
							}
						}
						beforeText = "";
						bufferIndex = 0;
						Utils.CleanUpCA(buffer);						
					}
					else
					{
						//* this should be a html entity
						buffer[bufferIndex] = chr;
						bufferIndex++;
					}
				}
				else if (inTag)
				{
					buffer[bufferIndex] = chr;
					bufferIndex++;
										
					if(Utils.StartsWith(buffer,"<!".ToCharArray(),0))
					{
						endToken = (">".ToCharArray());	
						findEndToken = true;
						inSetupTag = true;
						inTag = false;
					}
				}
				else
				{
					buffer[bufferIndex] = chr;
					bufferIndex++;
				}
			}

            beforeText = new string(buffer, 0, bufferIndex);
            if (!beforeText.Equals(""))
            {
                _allNodes.Add(new HtmlParser.Nodes.Text(beforeText));
            }

			ArrayList tagsToMatch = new ArrayList();
			ArrayList unusedElements = new ArrayList();
		
			//* Find end tags.
			foreach(Nodes.Element element in tags)
			{
				if (element.IsEndElement)
				{
					bool foundPreElement = false;
                    for (int i = tagsToMatch.Count - 1; (i >= 0) && (!foundPreElement); i--)
					{
                        Nodes.Element preElement = (Nodes.Element)tagsToMatch[i];
						if(preElement.TagName.ToLower() == element.TagName.ToLower())
						{
							preElement.EndElement = element;
                            tagsToMatch.Remove(preElement);
							foundPreElement = true;                            
						}
					}

					if(!foundPreElement)
					{
						unusedElements.Add(element);
					}                    
				}
				else
				{
                    tagsToMatch.Add(element);
				}
			}

			foreach(Nodes.Element element in unusedElements)
			{
				tags.Remove(element);
                _allNodes.Remove(element);
			}

			//* Find parent elements            
			Nodes.Element parentElement = null;
            _rootNodes = new NodeCollection();
			foreach(Nodes.Node element in _allNodes)
			{
				if (!(element is HtmlParser.Nodes.Element))
				{
					if (parentElement != null)
					{
						parentElement.AppendChild(element);
					}
					else
					{
						_rootNodes.Add(element);
					}
				}
				else
				{
					Nodes.Element tagElement = (Nodes.Element)element;
					if (!tagElement.IsEndElement)
					{
						if (parentElement == null)
						{
							_rootNodes.Add(tagElement);
						}
						else
						{
							parentElement.AppendChild(tagElement);
						}

						if (tagElement.EndElement != null)
						{
							parentElement = tagElement;
						}					
					}
					else
					{
						if (parentElement == null)
						{
						}
						else
						{
							parentElement = parentElement.Parent;
						}
					}					
				}
			}
		}

        public string GetHtml()
        {
            StringBuilder result = new StringBuilder();
            foreach (Nodes.Node node in RootNodes)
            {
                result.Append(node.GetHtml());
            }
            return result.ToString();
        }
	}
}
